package org.wejde.muel;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.swing.*;

/**
 * For use in annotation data collection from multiple users contributing to a
 * single operation. These annotations can help provide a context for the other
 * data collected on expeditions ranging from exploration to science, and help
 * provide a consistent set of vocabulary despite a diverse number of
 * contributors with varied specialties.
 * 
 * Based largely on the Scilog and later Periscope project developed for use by
 * the Institute for Exploration (IFE). This updated version is intended for
 * use by the NOAA Ship Okeanos Explorer and the E/V Nautilus but can be easily
 * adapted for use by other oceanographic research programs requiring online
 * multi-user real-time annotatoin collection.
 * 
 * @author Webb Pinner
 * @author Eric Martin
 * 
 */
public class muel extends JFrame implements ActionListener, KeyListener {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = -5162382938333551468L;

	/**
	 * Current version number of Muel.
	 */
	public static final String VERSION = "0.2";
	
	/**
	 * 
	 */
	private static final String DATEFORMAT = "yyyy-MM-dd";
	private static final String TIMEFORMAT = "HH:mm:ss.SSS";
	private static final String CHATTIMEFORMAT = "(HH:mm:ss)";

	/**
	 * 
	 */
	private JMenuBar menubar;
	private JMenu menu;
	private JMenuItem menuitem;
	
	/**
	 * The main pane for the JFrame
	 */
	private JPanel master;
	
	// The top order JPanels
	/**
	 * Contains things like CruiseID, DiveID, etc.
	 */
	private headerPane header;
	
	/**
	 * Contains the tabPane, additionalTextPane and
	 * chatroomPane.
	 */
	private mainPane main;
	
	/**
	 * Contains the chatroom panel.
	 */
	private rosterPane roster;
	
	/**
	 * Configuration information for the application.
	 */
	private muelConfigParser config;
	
	/**
	 * The multi-deminsional array of fields for the
	 * tabbed panels.
	 */
	private fields[][] mainFA;

	/**
	 * The array of Strings that contain the tab names. 
	 */
	private String[] tabNames;

	/**
	 * The array of fields in the header.
	 */
	private fields[] headerFA;
	
	/**
	 * Reference to the Submit Button
	 */
	private JButton submit;

	/**
	 * Reference to the Clear Button
	 */
	private JButton clear;
	
	/**
	 * Local Logging Client
	 */
	private localLogClient local;
	
	/**
	 * UDP Logging Client
	 */
	private udpLogClient udp;
	
	/**
	 * XMPP Logging Client
	 */
	private xmppLogClient xmpp;
	
	/**
	 * Default constructor method.
	 */
	public muel(muelConfigParser config, xmppLogClient xmpp) {
		super("Muel v"+ muel.VERSION + " " + config.getAppName());
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setMinimumSize(new Dimension(830,700));
		this.setSize(this.getMinimumSize());
		this.setResizable(true);
		
		this.config = config;
		
		// Add Menu Bar
		this.menubar = new JMenuBar();
		this.menu = new JMenu("File");
		this.menu.setMnemonic(KeyEvent.VK_F);
		this.menu.getAccessibleContext().setAccessibleDescription("The only menu in this program that has menu items");
		this.menubar.add(this.menu);
//		this.menuitem = new JMenuItem("Open Local Logfile",KeyEvent.VK_O);
//		if(!config.isLocalLogging()) {
//			this.menuitem.setEnabled(false);
//		}
//		this.menuitem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, ActionEvent.ALT_MASK));
//		this.menuitem.getAccessibleContext().setAccessibleDescription("Not yet implemented");
//		this.menuitem.addActionListener(this);
//		this.menu.add(this.menuitem);
		this.menuitem = new JMenuItem("Logout",KeyEvent.VK_X);
		if(!config.isXMPPLogging()) {
			this.menuitem.setEnabled(false);
		}
//		this.menuitem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, ActionEvent.ALT_MASK));
//		this.menuitem.getAccessibleContext().setAccessibleDescription("Not yet implemented");
//		this.menuitem = new JMenuItem("A text-only menu item",KeyEvent.VK_T);
//		this.menuitem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, ActionEvent.ALT_MASK));
//		this.menuitem.getAccessibleContext().setAccessibleDescription("Need to work on this");
		this.menuitem.addActionListener(this);
		this.menu.add(this.menuitem);
		this.setJMenuBar(this.menubar);
				
		// Get the field arrays and Tab Names
		headerFA = this.config.getHeaderFields();
		mainFA = this.config.getMainFields();
		tabNames = this.config.getTabNames();
		
		// Create the master JPanel
		this.master = new JPanel(new BorderLayout(10,10));
		this.master.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
	
		// Setup the header panel
		if(headerFA != null) {
			this.header = new headerPane(headerFA);
			this.header.setDelimiter(this.config.getMuelDelim());
		} else {
			this.header = null;
		}
		
		// Setup the main panel
		this.main = new mainPane(mainFA, tabNames);
		this.main.setDelimiter(this.config.getMuelDelim());
		
		// Get the references to the Clear and Submit
		// buttons.
		this.submit = this.main.getSubmit();
		this.clear = this.main.getClear();

		// Setup the ActionListeners for the two buttons.
		this.submit.addActionListener(this);
		this.clear.addActionListener(this);
		
		// If XMPP logging is enabled, initialize the
		// rosterPane.
		if(config.isXMPPLogging()) {
			this.roster = new rosterPane();
		}
		
		// Add the panels to the master panel
		if(this.header != null)
			this.master.add(BorderLayout.NORTH, this.header);
		
		this.master.add(BorderLayout.CENTER, this.main);
		if (config.isXMPPLogging()) {
			this.master.add(BorderLayout.EAST, this.roster);
		}
				
		// Used to link keyboard with Muel
		KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
	    manager.addKeyEventDispatcher(new MyDispatcher());
		
	    // If XMPP logging is enabled, initialize the
	    // XMPPLogClient object
	    if(config.isXMPPLogging()){
	    	this.xmpp = xmpp;
	    	this.xmpp.setChatroomPane(main.getChatroomPane());
	    	this.xmpp.setRosterPane(this.roster);
	    	this.xmpp.joinMUC(config.getChatroomName());
	    	this.roster.setMUC(this.xmpp.getMUC());
	    	this.roster.updateRoster();
	    }
	    // Else if UDP logging is enabled, initialize the
	    // UDPLogClient object
	    else if(config.isUDPLogging()) {
	    	this.udp = new udpLogClient(this.config.getServerName(), this.config.getServerPort());
//	    	this.setMinimumSize(new Dimension(630,900));
//	    	this.setSize(this.getMinimumSize());
	    }

	    // If UDP logging is enabled, initialize the
	    // LocalLogClient object
	    if(config.isLocalLogging()) {
	    	this.local = new localLogClient(this.config.getLocalLogFilename());
	    }
	    
		// Pack and display
		this.pack();
		this.setContentPane(this.master);
	}

	/**
	 * De-constructor method for muel
	 */
	public void destroy() {
		if(config.isXMPPLogging()) {
			xmpp.disconnect();
		}
	}
		
	/**
	 * Submit event to the logging clients.
	 */
	private void submit() {
		
		String eventText = "";
		
		// DEBUG
		//System.err.print("Recieved Submit: ");

		// Built the timestamp
		Date now = new Date();
		SimpleDateFormat logfmt = new SimpleDateFormat(muel.DATEFORMAT + this.config.getMuelDelim() + muel.TIMEFORMAT);
		SimpleDateFormat chatfmt = new SimpleDateFormat(muel.CHATTIMEFORMAT);
				
		// Retrieve the values from all the input fields in
		// the header.
		String headerText = "";
		if(this.header != null) {
			headerText = this.header.getFieldValues();
		}
		
		// Retrieve the values from all the input fields in
		// the main window.  Remember this is only for the
		// viewable tab
		String mainText = this.main.getFieldValues();		

		// Finalized the eventText.
		// If there was no text in the main window, do not
		// submit the event.
		if(mainText.isEmpty()) {

			// DEBUG
			//System.err.println("No Message");
		}
		// Else, combine the headerText and mainText.
		// Strip off the delimiter from the end of the mainText
		else {
			if(this.header != null){
				eventText += headerText;
			}
				
			if(mainText.endsWith(this.config.getMuelDelim())) {
				eventText += mainText.substring(0, mainText.length()-1);	
			} else {
				eventText += mainText;
			}

			// DEBUG
			//System.err.println(eventText.trim());
			
			// If local logging is enabled, send the
			// eventText to the localLogClient
			if(this.config.isLocalLogging()) {
				if(!this.local.submitEvent(logfmt.format(now) + config.getMuelDelim() + this.config.getUsername() + config.getMuelDelim() + eventText + this.config.getMuelTerm())) {
					this.main.getChatroomPane().addEventMsg(this.local.getErrorMsg() + this.config.getMuelTerm());
				}
			}
			
			if(this.config.isXMPPLogging()) {
				if (!this.xmpp.submitEvent(eventText + this.config.getMuelTerm())) {
					if(config.isLocalLogging()) {
						this.main.getChatroomPane().addEventMsg(" Don't panic, event was logged locally." + this.config.getMuelTerm());
						this.main.getChatroomPane().addEventMsg(chatfmt.format(now) + " " + this.config.getUsername() + ": " + eventText + this.config.getMuelTerm());
					} else {
						this.main.getChatroomPane().addEventMsg(" Panic, local logging disabled!." + this.config.getMuelTerm());
						this.main.getChatroomPane().addEventMsg(chatfmt.format(now) + " " + this.config.getUsername() + ": " + eventText + this.config.getMuelTerm());
					}
				}
			} else if(this.config.isUDPLogging()) {
				this.udp.submitEvent(logfmt.format(now) + config.getMuelDelim() + this.config.getUsername() + config.getMuelDelim() + eventText + this.config.getMuelTerm());
				this.main.getChatroomPane().addEventMsg(chatfmt.format(now) + " " + this.config.getUsername() + ": " + eventText + this.config.getMuelTerm());
			}
		}
	}

	/**
	 * Clear the clear-able input fields in muel.
	 */
	private void clear() {
		// DEBUG
		//System.err.println("Recieved Clear");
		this.main.clearFields();
	}
	
	/**
	 * Handle button presses.
	 * 
	 * @param evt action event
	 */
	@Override
	public void actionPerformed(ActionEvent evt) {
		if (evt.getActionCommand().equals("Submit")) {
			this.submit();
			this.clear();
		} else if (evt.getActionCommand().equals("Clear")) {
			this.clear();
//		} else if (evt.getActionCommand().equals("Open Local Logfile")) {
			// DEBUG
			//System.err.println("Open Local Logfile");
		} else if (evt.getActionCommand().equals("Logout")) {
			// DEBUG
			//System.err.println("Logout");
			if(config.isXMPPLogging()) {
				xmpp.disconnect();
			}
			this.setVisible(false);
		}
	}

	/**
	 * Handle key presses
	 * 
	 * @param evt key event
	 */
	@Override
	public void keyPressed(KeyEvent evt) {
		// TODO Auto-generated method stub
	}

	/**
	 * Handle key releases
	 * 
	 * @param evt key event
	 */
	@Override
	public void keyReleased(KeyEvent evt) {
		// TODO Auto-generated method stub
	}

	/**
	 * Handle key typing
	 * 
	 * @param evt key event
	 */
	@Override
	public void keyTyped(KeyEvent evt) {
		// TODO Auto-generated method stub
	}
	
	/**
	 * All keyboard key event are redirected here. This is
	 * where thing like hitting the enter and esc keys are
	 * processed. 
	 * 
	 * @author webbpinner
	 *
	 */
	private class MyDispatcher implements KeyEventDispatcher {
	    @Override
	    public boolean dispatchKeyEvent(KeyEvent e) {
	        if (e.getID() == KeyEvent.KEY_PRESSED) {
	        	
	        	// DEBUG
	            //System.err.println(e.getKeyCode());
	        	
	            // The ENTER Key
	            if(e.getKeyCode() == 10) {
	            	submit();
	        		clear();
	        	} 
	            
	            // The ESC Key
	            else if(e.getKeyCode() == 27) {
	        		clear();
	        	}
	            
	        } else if (e.getID() == KeyEvent.KEY_RELEASED) {
	        //    TODO Add code here
	        } else if (e.getID() == KeyEvent.KEY_TYPED) {
	        //    TODO Add code here
	        }
	        return false;
	    }
	}
}
